﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorldBankBBS.Comms;
using WorldBankBBS.Data;
using WorldBankBBS.Handlers;
using WorldBankBBS.Input;
using WorldBankBBS.Resources;
using WorldBankBBS.Translators;

namespace WorldBankBBS.Logic
{
    public class LoginHandler
    {
        private readonly ITranslator _translator = null;
        private readonly Session _session = null;

        public LoginHandler(Session session)
        {
            _session = session;

            switch (session.Encoding)
            {
                case "PETSCII":
                    _translator = new CbmTranslator() {CurrentSession = session};
                    break;

                case "ANSI":
                    _translator = new AnsiTranslator() {CurrentSession = session};
                    break;

                default:
                    _translator = new AsciiTranslator() {CurrentSession = session};
                    break;
            }
        }
        public event TextInputHandler TextInput;

        public static void OnLogin(object sender, Session session)
        {
            var handler = new LoginHandler(session);

            var pre = BbsDispatcher.GetDispatcher(session).GetInvoker("WorldBankBBS:WorldBankBBS.Logic.BbsMain.PreGuestLogin");
            var guest = BbsDispatcher.GetDispatcher(session).GetInvoker("WorldBankBBS:WorldBankBBS.Logic.BbsMain.GuestLogin");
            var post = BbsDispatcher.GetDispatcher(session).GetInvoker("WorldBankBBS:WorldBankBBS.Logic.BbsMain.PostGuestLogin");

            session.LoginCompleted = handler.Login(0, 5, pre, guest, post);
        }

        public static void OnLoginFailed(object sender, Session session)
        {
            Log.WriteLogEntry("Audit", "Session Login Failed.");
        }

        public bool Login(int count, int max,
            EventHandler<Session> onPreGuestLoginHandler,
            EventHandler<Session> onGuestLogin,
            EventHandler<Session> onPostGuestLoginHandler)
        {
            if (count == max) return false;

            var inputHandler = new InputHandler();

            TextInput += inputHandler.OnTextInput;

            var buffer = new byte[1024];
            var user = "";
            var password = "";

            var l = 0;

            if (_session.Encoding != "ASCII")
            {
                l = _translator.StringToBytes(buffer, StringTable.BANNER_LOGIN);
                _session.Connection.SendData(buffer, 0, l);

                l = _translator.StringToBytes(buffer, StringTable.UP4_RIGHT11);
                _session.Connection.SendData(buffer, 0, l);

                l = _translator.StringToBytes(buffer, "{WHITE}");
                _session.Connection.SendData(buffer, 0, l);

                if (TextInput != null)
                {
                    user = TextInput(_session, _translator, _session.Connection, 20, false);
                    if (user == "?")
                    {
                        DisplayUsers();

                        TextInput -= inputHandler.OnTextInput;
                        return Login(count, max, onPreGuestLoginHandler, onGuestLogin, onPostGuestLoginHandler);

                    }
                    else if (user == string.Empty)
                    {
                        user = "Guest";
                        var g = new byte[5];
                        _translator.StringToBytes(g, "Guest");
                        _session.Connection.SendData(g);
                    }
                }

                l = _translator.StringToBytes(buffer, StringTable.DOWN2_RIGHT11);
                _session.Connection.SendData(buffer, 0, l);

                l = _translator.StringToBytes(buffer, "{WHITE}");
                _session.Connection.SendData(buffer, 0, l);

                if (TextInput != null)
                {
                    password = TextInput(_session, _translator, _session.Connection, 20, true);
                    if (password == string.Empty)
                    {
                        password = "Guest";
                    }
                }

                l = _translator.StringToBytes(buffer, "{%BOL}{DOWN}{DOWN}");
                _session.Connection.SendData(buffer, 0, l);

            }
            else
            {
                l = _translator.StringToBytes(buffer, StringTable.BANNER_LOGIN_ASCII_1);
                _session.Connection.SendData(buffer, 0, l);

                if (TextInput != null)
                {
                    user = TextInput(_session, _translator, _session.Connection, 20, false);
                    if (user == "?")
                    {
                        DisplayUsers();

                        TextInput -= inputHandler.OnTextInput;
                        return Login(count, max, onPreGuestLoginHandler, onGuestLogin, onPostGuestLoginHandler);

                    }
                    else if (user == string.Empty)
                    {
                        user = "Guest";
                        var g = new byte[5];
                        _translator.StringToBytes(g, "Guest");
                        _session.Connection.SendData(g);
                    }
                }

                l = _translator.StringToBytes(buffer, StringTable.BANNER_LOGIN_ASCII_2);
                _session.Connection.SendData(buffer, 0, l);

                if (TextInput != null)
                {
                    password = TextInput(_session, _translator, _session.Connection, 20, true);
                    if (password == string.Empty)
                    {
                        password = "Guest";
                    }
                }

                l = _translator.StringToBytes(buffer, StringTable.BANNER_LOGIN_ASCII_3);
                _session.Connection.SendData(buffer, 0, l);

            }



            if (user.ToLower() != "guest")
            {
                var u = User.LoadByUsername(user, password);

                if (u == null)
                {
                    TextInput -= inputHandler.OnTextInput;

                    return Login(++count, max, onPreGuestLoginHandler, onGuestLogin, onPostGuestLoginHandler);
                }

                _session.User = u;
                _session["ScreenWidth"] = u.UserPreference != null ? u.UserPreference.ScreenWidth : (_session.Encoding == "PETSCII" ? 40 : 80);
                _session["ScreenHeight"] = u.UserPreference != null ? u.UserPreference.ScreenHeight : (_session.Encoding == "PETSCII" ? 25 : 24);
                _session["HelpLevel"] = u.UserPreference != null ? u.UserPreference.HelpLevel : 1;

                u.IsLoggedIn = true;
                
                using (var m = new ModelHelper())
                {
                    m.BeginNewContext();
                    var s = m.Context.Sessions.FirstOrDefault(x => x.SessionUID == _session.SessionUID);
                    if (s != null)
                    {
                        s.UserUID = u.UserUID;
                    }
                    else
                    {
                        m.AddToSessions(new Data.Session
                        {
                            Encoding = _session.Encoding,
                            RemoteIP = ((ITcpConnection)_session.Connection).ClientAddress.ToString(),
                            SessionUID = _session.SessionUID,
                            UserUID = u.UserUID,
                            StartedOn = DateTime.UtcNow
                        });
                    }
                    m.Context.SaveChanges();
                }

                TextInput -= inputHandler.OnTextInput;
                return true;
            }
            else
            {

                if (onGuestLogin != null)
                {
                    if (onPreGuestLoginHandler != null) onPreGuestLoginHandler(this, _session);
                    
                    TextInput -= inputHandler.OnTextInput;

                    onGuestLogin(this, _session);
                    var result = _session.GuestLoginCompleted;

                    if (!result)
                        return Login(count + 1, max, onPreGuestLoginHandler, onGuestLogin, onPostGuestLoginHandler);

                    if (onPostGuestLoginHandler != null) onPostGuestLoginHandler(this, _session);
                    
                    return true;
                }
                else
                {
                    TextInput -= inputHandler.OnTextInput;
                    return false;
                }
            }
        }

        private void DisplayUsers()
        {
            var buffer = new byte[1024];
            var l = _translator.StringToBytes(buffer, StringTable.LOGIN_KNOWN_USERNAMES);
            _session.Connection.SendData(buffer, 0, l);

            using (var mh = new ModelHelper())
            {
                mh.BeginNewContext();

                foreach (var user in mh.Context.Users.OrderBy(x => x.UserName))
                {
                    l = _translator.StringToBytes(buffer, StringTable.LOGIN_KNOWN_USER);
                    _session.Connection.SendData(buffer, 0, l);

                    l = _translator.StringToBytes(buffer, user.UserName);
                    _session.Connection.SendData(buffer, 0, l);
                }

                l = _translator.StringToBytes(buffer, "{CRLF}");
                _session.Connection.SendData(buffer, 0, l);
            }
        }
    }
}
    